package com.algo.GA;

import lombok.Data;
import lombok.extern.log4j.Log4j;

import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

/**
 * @Author: guoxing
 * @Email: gx_study2022@163.com
 * @Description:
 * @Date: 2024/6/11 19:37
 * @ClassName: GA
 **/
@Log4j
@Data
public class GA {
    //保存每一代最好的个体
    private  Individual bestIndividual;
    private List<Individual> allIndividual=new ArrayList<Individual>();
    //种群初始化计算适应度值并为个体排序
    /**
     * @Description 初始化种群计算适应度值并按照适应度值进行升序排序
     * @author guoxing
     * @date 2024/6/11 21:52
     * @return List<Individual>
     */
    public List<Individual> initPop(){
        List<Individual> pop=new ArrayList<>();
        Random rand = new Random();
        for (int i = 0; i <Param.POPSIZE;i++){
            List indL=new ArrayList();
            for (int j = 0; j < Param.INDIVIDUALLENGTH; j++) {
                indL.add(rand.nextInt(2));
            }
            Individual ind=new Individual();
            ind.setIndividual(indL);
            ind.setFitness();
            pop.add(ind);
        }
        Collections.sort(pop);
//        bestIndividual=pop.get(pop.size()-1);
        return pop;
    }

    //选择操作
    /**
     * @Description 轮盘赌选择操作，对适应度值较高的个体进行筛选
     * @author guoxing
     * @date 2024/6/11 22:19
     * @param pop 待轮盘赌选择操作的种群
     * @return List<Individual> 已经经过轮盘赌选择操作的个体
     */
    public List<Individual> selectPop(List<Individual> pop){
        double allFitness=0;
        List<Individual> popSelectPop=new ArrayList<Individual>();
        List<Double> selectIndex=new ArrayList<Double>();
        Random random = new Random();
        for (int i = 0; i < pop.size(); i++){
            allFitness+=pop.get(i).getFitness();
        }
        for (int i = 0; i < pop.size(); i++){
            selectIndex.add(allFitness*random.nextDouble());
        }
        Collections.sort(selectIndex);
        for (int i = 0; i < pop.size(); i++) {
            double sum=0;
            for (Individual individual:pop){
                sum+=individual.getFitness();
                if(sum>selectIndex.get(i)){
                    popSelectPop.add(individual);
                    break;
                }
            }
        }
        return popSelectPop;
    }


    //交叉操作
    /***
     * @Description 交叉操作
     * @author guoxing
     * @date 2024/6/12 9:49
     * @param pop 待交叉的种群
     * @return List<Individual> 交叉操作完成后的种群
     */
    public List<Individual> crossPop(List<Individual> pop){
        List<Individual> crossPop=new ArrayList<>();
        Random random = new Random();
        for (int i = 0; i <pop.size()-1; i+=2){
            //达到交叉条件
            if (random.nextDouble() <=Param.CP) {
//                System.out.println("交叉了"+i);
                int index=random.nextInt(Param.INDIVIDUALLENGTH);
                Individual individual1=pop.get(i);
                Individual individual2=pop.get(i);
                List<Integer> indL1=new ArrayList<>();
                List<Integer> indL2=new ArrayList<>();
                for (int j = 0; j < index; j++) {
                    indL1.add(individual1.getIndividual().get(j));
                    indL2.add(individual2.getIndividual().get(j));
                }
                for (int j = index; j < Param.INDIVIDUALLENGTH; j++){
                    indL1.add(individual2.getIndividual().get(j));
                    indL2.add(individual1.getIndividual().get(j));
                }
                Individual newIndividual1=new Individual();
                Individual newIndividual2=new Individual();
                newIndividual1.setIndividual(indL1);
                newIndividual2.setIndividual(indL2);
                crossPop.add(newIndividual1);
                crossPop.add(newIndividual2);
            }else {
                crossPop.add(pop.get(i));
                crossPop.add(pop.get(i+1));
            }
        }
        Collections.sort(crossPop);
        return crossPop;
    }
    //变异操作
    /***
     * @Description 变异操作
     * @author guoxing
     * @date 2024/6/12 19:11
     * @param pop 待变异的种群
     * @return List<Individual> 已经变异完成的种群
     */
    public List<Individual> mutationPop(List<Individual> pop){
        List<Individual> mutationPop=new ArrayList<>();
        Random random = new Random();
        for(int i = 0; i < pop.size(); i++){
            List<Integer> individual=new ArrayList<>();
            //达到变异条件
            if (random.nextDouble()<=Param.MP){
//                System.out.println("变异了"+i);
                int index = random.nextInt(Param.INDIVIDUALLENGTH);
                int index1 = random.nextInt(Param.INDIVIDUALLENGTH);
                int MNumber=random.nextInt(2);
                int MNumber1=random.nextInt(2);
//                System.out.println("变异位置为"+index+"￥￥"+index1);
                Individual ind=new Individual();
                for (int j = 0; j < pop.get(i).getIndividual().size(); j++) {
                    individual.add(pop.get(i).getIndividual().get(j));
                }

                individual.set(index,MNumber);
                individual.set(index1,MNumber1);
                ind.setIndividual(individual);
                mutationPop.add(ind);
            }else {
                mutationPop.add(pop.get(i));
            }
        }
        Collections.sort(mutationPop);
        return mutationPop;
    }
    //
    //
    //
    //
    //
    //
    public static void main(String[] args) {
        Individual bestIndividual;
        List<Individual> allIndividual=new ArrayList<Individual>();
        GA ga=new GA();
        //生成初始种群
        List<Individual> pop=ga.initPop();
        for (int i = 0; i < Param.ITERATIONS; i++) {
            //保留最好个体
            bestIndividual=pop.get(pop.size()-1);
            allIndividual.add(bestIndividual);
            //选择操作
            List<Individual> selectPop=ga.selectPop(pop);
            //交叉操作
            List<Individual> crossPop=ga.crossPop(selectPop);
            //变异操作
            List<Individual> mutationPop=ga.mutationPop(crossPop);
            mutationPop.set(0,bestIndividual);
            Collections.sort(mutationPop);
            for (int j = 0; j < mutationPop.size(); j++) {
                pop.set(j,mutationPop.get(j));
            }
        }
        for (int j = 0; j < allIndividual.size(); j++) {
            System.out.println(allIndividual.get(j));
        }
    }
}
